﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Caixa.Model;
using Caixa.DB;
using System.Data.Common;
using System.Collections;
using System.Data;

namespace Caixa.DAO
{
	public static class MovimentacaoDAO
	{
		#region " Methods "

		#region " Get Methods "

		public static Movimentacao GetByID(int id)
		{
			Movimentacao m = null;

			using (DAHelper db = new DAHelper())
			{
				string SQL = string.Format(@"
					SELECT 
						{1}, {2}, {3}, {4}, {5}, {6}, {7}
					FROM {0}
					WHERE
						{1} = :{1}
				",
					Movimentacao.Fields.TABELA,
					Movimentacao.Fields.ID,
					Movimentacao.Fields.IDCONTA,
					Movimentacao.Fields.TIPO,
					Movimentacao.Fields.DESCRICAO,
					Movimentacao.Fields.DATA,
					Movimentacao.Fields.VALOR,
					Movimentacao.Fields.SALDO
				);

				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.ID, id));

				// Executa a query no Banco de dados
				IEnumerator data = db.SelectSqlDr(SQL, parms.ToArray()).GetEnumerator();

				// Converte o resultado do Banco de dados em objeto
				m = DAHelper.ToModel<Movimentacao>(data);
			}

			return m;
		}

		public static List<Movimentacao> GetByData(Conta c, DateTime data)
		{
			return GetByPeriodo(c, data, data);
		}

		public static List<Movimentacao> GetByPeriodo(Conta c, DateTime dataInicio, DateTime dataFim)
		{
			List<Movimentacao> list = new List<Movimentacao>();

			// Formata a hora, minuto, segundo e milésimos para não dar problema na consulta
			dataInicio = new DateTime(dataInicio.Year, dataInicio.Month, dataInicio.Day, 0, 0, 0, 0);
			dataFim = new DateTime(dataFim.Year, dataFim.Month, dataFim.Day, 23, 59, 59, 999);

			using (DAHelper db = new DAHelper())
			{
				string SQL = string.Format(@"
					SELECT 
						{1}
					FROM {0}
					WHERE
						{2} = :{2} AND
						{3} BETWEEN :{3}Inicio AND :{3}Fim
				",
					Movimentacao.Fields.TABELA,
					Movimentacao.Fields.ID,
					Movimentacao.Fields.IDCONTA,
					Movimentacao.Fields.DATA
				);

				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, c.ID));
				parms.Add(db.GetParameter(string.Format("{0}Inicio", Movimentacao.Fields.DATA), dataInicio, DbType.Date));
				parms.Add(db.GetParameter(string.Format("{0}Fim", Movimentacao.Fields.DATA), dataFim, DbType.Date));

				// Executa a query no Banco de dados				
				DataTable dt = db.SelectSqlDt(SQL, parms.ToArray());

				// Restaga a instâcia do objeto
				foreach (DataRow row in dt.Rows)
				{
					int movID;
					int.TryParse(string.Format("{0}", row[Movimentacao.Fields.ID]), out movID);
					Movimentacao m = GetByID(movID);
					if (m != null)
						list.Add(m);
				}
			}

			return list;
		}

		public static double GetSaldoAnterior(Movimentacao m)
		{
			double saldo = 0;

			using (DAHelper db = new DAHelper())
			{
				string SQL = string.Format(@"
					SELECT TOP 1
						{3}
					FROM {0}
					WHERE
						({1} < {4}) 
						AND ({2} <= :{2})
					ORDER BY 
						{2} DESC, 
						{1} DESC
				",
				 Movimentacao.Fields.TABELA,
				 Movimentacao.Fields.ID,
				 Movimentacao.Fields.DATA,
				 Movimentacao.Fields.SALDO,
				 m.ID);

				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));

				// Executa a query no Banco de dados e retorna se foi atualizado com sucesso
				double.TryParse(string.Format("{0:0.00}", db.SelectSqlSc(SQL, parms.ToArray())), out saldo);
			}

			return saldo;
		}

		private static Dictionary<int, double> GetMovAcertarSaldo(Movimentacao m)
		{
			Dictionary<int, double> dictionary = new Dictionary<int, double>();
			dictionary.Add(m.ID, (m.Valor * m.Fator));

			using (DAHelper db = new DAHelper())
			{
				string SQL = string.Format(@"
					SELECT
						{1},
						({3} * {4}) AS {3}
					FROM {0}
					WHERE
						({1} > {5})
						AND ({2} >= :{2})
					ORDER BY 
						{2} ASC, 
						{1} ASC
				",
				 Movimentacao.Fields.TABELA,
				 Movimentacao.Fields.ID,
				 Movimentacao.Fields.DATA,
				 Movimentacao.Fields.VALOR,
				 Movimentacao.Fields.TIPO,
				 m.ID);

				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));

				// Executa a query no Banco de dados e retorna se foi atualizado com sucesso
				DbDataReader dr = db.SelectSqlDr(SQL, parms.ToArray());
				while (dr.Read())
				{
					double valor;
					double.TryParse(string.Format("{0}", dr.GetValue(dr.GetOrdinal(Movimentacao.Fields.VALOR))), out valor);
					dictionary.Add(
						dr.GetInt32(dr.GetOrdinal(Movimentacao.Fields.ID)),
						valor
					);
				}
				dr.Close();
			}

			return dictionary;
		}

		#endregion

		#region " DDL Methods "

		public static double AtualizaSaldo(Movimentacao m)
		{
			double saldoAtual = 0;

			// Obtém todas as movimentações que terão que acertar os saldos
			Dictionary<int, double> dicMovAcertar = GetMovAcertarSaldo(m);

			// Recupera o saldo da movimentação anterior a esta
			double saldoAnterior = GetSaldoAnterior(m);

			// Acerta o saldo de cada movimentação
			using (DAHelper db = new DAHelper())
			{
				foreach (KeyValuePair<int, double> movItem in dicMovAcertar)
				{
					saldoAnterior = Math.Round(saldoAnterior + movItem.Value, 2);
					saldoAtual = saldoAnterior;

					string SQL = string.Format(@"
						UPDATE {0} SET
							{2} = :{2}				
						WHERE						
							{1} = {3}
					",
					 Movimentacao.Fields.TABELA,
					 Movimentacao.Fields.ID,
					 Movimentacao.Fields.SALDO,
					 movItem.Key);

					// Prepara os parâmetros da Query
					List<DbParameter> parms = new List<DbParameter>();
					parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, saldoAtual, DbType.Double));

					// Executa a query no Banco de dados
					db.ExecuteSql(SQL, parms.ToArray());
				}
			}

			return saldoAtual;
		}

		public static int Insert(Movimentacao m)
		{
			int newID = 0;

			using (DAHelper db = new DAHelper())
			{
				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, m.IDConta));
				parms.Add(db.GetParameter(Movimentacao.Fields.TIPO, m.Fator));
				parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));
				parms.Add(db.GetParameter(Movimentacao.Fields.DESCRICAO, m.Descricao));
				parms.Add(db.GetParameter(Movimentacao.Fields.VALOR, m.Valor, DbType.Double));
				parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, m.Saldo, DbType.Double));

				// Executa a query no Banco de dados e retorna o ID do novo registro
				newID = db.InsertSql(Movimentacao.Fields.TABELA, parms.ToArray());
			}

			return newID;
		}

		public static bool Update(Movimentacao m)
		{
			bool success = false;

			using (DAHelper db = new DAHelper())
			{
				// Prepara os parâmetros da Query
				List<DbParameter> parms = new List<DbParameter>();
				parms.Add(db.GetParameter(Movimentacao.Fields.IDCONTA, m.IDConta));
				parms.Add(db.GetParameter(Movimentacao.Fields.TIPO, m.Fator));
				parms.Add(db.GetParameter(Movimentacao.Fields.DATA, m.Data, DbType.Date));
				parms.Add(db.GetParameter(Movimentacao.Fields.DESCRICAO, m.Descricao));
				parms.Add(db.GetParameter(Movimentacao.Fields.VALOR, m.Valor, DbType.Double));
				parms.Add(db.GetParameter(Movimentacao.Fields.SALDO, m.Saldo, DbType.Double));

				// Define a cláusula where do Update
				string sWhere = string.Format("{0} = {1}", Movimentacao.Fields.ID, m.ID);

				// Executa a query no Banco de dados e retorna se foi atualizado com sucesso
				success = db.UpdateSql(Movimentacao.Fields.TABELA, parms.ToArray(), sWhere);
			}

			return success;
		}

		public static bool Delete(Movimentacao m)
		{
			bool success = false;

			using (DAHelper db = new DAHelper())
			{
				// Define a cláusula where do Delete
				string sWhere = string.Format("{0} = {1}", Movimentacao.Fields.ID, m.ID);

				// Executa a query no Banco de dados e retorna se o registro foi excluido com sucesso
				success = db.DeleteSql(Movimentacao.Fields.TABELA, sWhere);
			}

			return success;
		}

		#endregion

		#endregion
	}
}
